package edu.uw.cs.cse461.sp12.OS;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import edu.uw.cs.cse461.sp12.DB461.DB461.DB461Exception;
import edu.uw.cs.cse461.sp12.DB461.DB461.RecordSet;
import edu.uw.cs.cse461.sp12.OS.HTTPDService.HTTPProvider;
import edu.uw.cs.cse461.sp12.SNetDB.SNetDB461;
import edu.uw.cs.cse461.sp12.SNetDB.SNetDB461.CommunityRecord;
import edu.uw.cs.cse461.sp12.SNetDB.SNetDB461.Photo;
import edu.uw.cs.cse461.sp12.SNetDB.SNetDB461.PhotoRecord;
import edu.uw.cs.cse461.sp12.util.Base64;
import edu.uw.cs.cse461.sp12.util.Log;

public class SNetService extends RPCCallable implements HTTPProvider {
	private static final String TAG = "SNetService";
	
	/* RPC METHOD VARIABLES */
	private RPCCallableMethod<SNetService> fetchUpdates;
	private RPCCallableMethod<SNetService> fetchPhoto;
	
	public SNetService() throws Exception {
		// Set up RPC methods
		RPCService rpc = (RPCService) OS.getService("rpc");
		this.fetchUpdates = new RPCCallableMethod<SNetService>(this, "fetchUpdates");
		rpc.registerHandler(servicename(), this.fetchUpdates.method.getName(), this.fetchUpdates);
		this.fetchPhoto = new RPCCallableMethod<SNetService>(this, "fetchPhoto");
		rpc.registerHandler(servicename(), this.fetchPhoto.method.getName(), this.fetchPhoto);
	}
	
	@Override
	public void shutdown() {
		// Nothing to do...
	}
	
	@Override
	public String servicename() {
		return "snet";
	}
	
	// {communityupdates: {MemberField, ...}, photoupdates: [int, ...]} fetchUpdates({community:{MemberField,...}, needphotos:[int, ...]})
	public JSONObject fetchUpdates(JSONObject json) throws JSONException, DB461Exception {
		JSONObject community = json.getJSONObject("community");
		JSONArray needPhotos = json.getJSONArray("needphotos");
		return _fetchUpdates(community, needPhotos);
	}
	
	private JSONObject _fetchUpdates(JSONObject community, JSONArray needPhotos) throws JSONException, DB461Exception {

		// Update our database with theirs and find all updates that we have for them
		SNetControl.updateMembers(community);
		JSONObject communityUpdates = SNetControl.getMembersJSON(community);

		
		
		// Find out which photos we have that they requested
		Set<Integer> photoUpdates = new HashSet<Integer>();
		
		// Check their originally needed photos
		for (int i = 0; i < needPhotos.length(); i++) {
			int hash = needPhotos.getInt(i);
			checkPhotoUpdateable(photoUpdates, hash);
		}
		
		// Check our community updates and add all the photos from them
		Iterator iterator = communityUpdates.keys();
		while (iterator.hasNext()) {
			String memberName = (String) iterator.next();
			JSONObject memberInfo = communityUpdates.getJSONObject(memberName);
			checkPhotoUpdateable(photoUpdates, memberInfo.getInt("myphotohash"));
			checkPhotoUpdateable(photoUpdates, memberInfo.getInt("chosenphotohash"));
		}
		
		// Go through photoUpdates and put in JSONArray
		JSONArray photoUpdateResult = SNetControl.toJSONArray(photoUpdates);
		
		JSONObject result = new JSONObject();
		result.put("communityupdates", communityUpdates);
		result.put("photoupdates", photoUpdateResult);
		return result;
	}

	private void checkPhotoUpdateable(Set<Integer> photoUpdates, int hash)
			throws DB461Exception {
		Log.d(TAG, "Checking photo: " + hash);
		if (SNetControl.getPhoto(hash) != null) {
			// Have the photo
			Log.d(TAG, "Adding " + hash + " to photoUpdates");
			photoUpdates.add(hash);
		}
	}
	
	// {photohash: int, photodata: encodedData} fetchPhoto(int hash)
	public JSONObject fetchPhoto(JSONObject json) throws JSONException, IOException, DB461Exception {
		try {
			int hash = json.getInt("photohash");
			return _fetchPhoto(hash);
		} catch (JSONException e) {
			Log.d(TAG, "Caught Exception: " + e.getMessage());
			throw e;
		} catch (IOException e) {
			Log.d(TAG, "Caught Exception: " + e.getMessage());
			throw e;
		} catch (DB461Exception e) {
			Log.d(TAG, "Caught Exception: " + e.getMessage());
			throw e;
		}
	}
	
	private JSONObject _fetchPhoto(int photoHash) throws JSONException, IOException, DB461Exception {
		// TODO: Implement
		
		JSONObject result = new JSONObject();
		File photoFile = SNetControl.getPhoto(photoHash);
		if (photoFile != null) {
			String photoData = Base64.encodeFromFile(photoFile.getAbsolutePath());
			
			result.put("photohash", photoHash);
			result.put("photodata", photoData);
		} else {
			throw new DB461Exception("Caller requested photo " + photoHash + " but I don't have it.");
		}
		return result;
	}

	@Override
	public String httpServe(String[] uriArray) {
		return SNetControl.dumpDB();
	}
}
